from flask import Flask, request, session, url_for, redirect, \
     render_template, g, flash
import sqlite3
from flask_limiter import Limiter
from utils import Config
import numpy as np
import os
import time
import pickle
from jieba import cut_for_search
from werkzeug import check_password_hash, generate_password_hash
import datetime
app = Flask(__name__)
app.config.from_object(__name__)
app.secret_key = 'yaozeping'
app.config['SESSION_TYPE'] = 'filesystem'


def connect_db():
  sqlite_db = sqlite3.connect(Config.database_path)
  sqlite_db.row_factory = sqlite3.Row # to return dicts rather than tuples
  return sqlite_db

def query_db(query, args=(), one=False):
  """Queries the database and returns a list of dictionaries."""
  cur = g.db.execute(query, args)
  rv = cur.fetchall()
  return (rv[0] if rv else None) if one else rv

def get_user_id(username):
  """Convenience method to look up the id for a username."""
  rv = query_db('select user_id from user where username = ?',
                [username], one=True)
  return rv[0] if rv else None

#limiter = Limiter(app, global_limits=["100 per hour", "20 per minute"])
@app.route("/")
def intmain():

    keys = db.keys()

    sorted_keys = sorted(keys, key=lambda x: db[x]['time'], reverse=True)
    news = [db[pid] for pid in sorted_keys[:100]]
    ctx = default_context(news, render_format='recent',
                       msg='Showing most recent tech news:')
    return render_template('main.html', **ctx)
@app.before_request
def before_request():
  g.db = connect_db()
  g.user = None
  if 'user_id' in session:
    g.user = query_db('select * from user where user_id = ?',
                      [session['user_id']], one=True)

@app.route('/login', methods=['POST'])
def login():
  """ logs in the user. if the username doesn't exist creates the account """
  if not request.form['username']:
    flash('You have to enter a username')
  elif not request.form['password']:
    flash('You have to enter a password')
  elif get_user_id(request.form['username']) is not None:
    # username already exists, fetch all of its attributes
    user = query_db('''select * from user where
          username = ?''', [request.form['username']], one=True)
    if check_password_hash(user['pw_hash'], request.form['password']):
      # password is correct, log in the user
      session['user_id'] = get_user_id(request.form['username'])
      flash('User ' + request.form['username'] + ' logged in.')
    else:
      # incorrect password
      flash('User ' + request.form['username'] + ' already exists, wrong password.')
  else:
    # create account and log in
    creation_time = int(time.time())
    g.db.execute('''insert into user (username, pw_hash, creation_time) values (?, ?, ?)''',
      [request.form['username'], 
      generate_password_hash(request.form['password']), 
      creation_time])
    user_id = g.db.execute('select last_insert_rowid()').fetchall()[0][0]
    g.db.commit()

    session['user_id'] = user_id
    flash('New account %s created' % (request.form['username'], ))

  return redirect(url_for('intmain'))
@app.route('/logout')
def logout():
  session.pop('user_id', None)
  flash('You were logged out')
  return redirect(url_for('intmain'))

@app.route('/recommend', methods=['GET'])
def recommend():
  """ return user's svm sorted list """
  if g.user:
    msg = ''
  else:
    msg = 'You must be logged in.'
  news = news_from_svm()
  ctx = default_context(news, render_format='recommend', msg=msg)
  return render_template('main.html', **ctx)

@app.route('/history', methods=['GET'])
def history():

  """ render user's library """
  news = []
  if g.user:
    msg = ''
    news_ids = query_db('''select news_id from history where
              user_id = ?''', [session['user_id']])

    news_ids = [news_ids[i][0] for i in xrange(len(news_ids))]
    news = [db[news_id] for news_id in news_ids]
  else:
    msg = 'You must be logged in.'

  ctx = default_context(news, render_format='history', msg=msg)
  return render_template('main.html', **ctx)

@app.route("/search", methods=['GET'])
def search():
  q = request.args.get('q', '') # get the search request
  news = news_search(q) # perform the query and get sorted documents
  ctx = default_context(news, render_format="search")
  return render_template('main.html', **ctx)


@app.route("/redirect", methods=['GET'])
def redirect_to():
  url = request.args.get('url')
  news_id = int(request.args.get('id'))
  if g.user:
    uid = session['user_id']
    update_time = int(time.time())
    g.db.execute('''insert into history (news_id, user_id, update_time) values (?, ?, ?)''',
                 [news_id,
                  uid,
                  update_time])
    g.db.commit()
  return redirect(url)

def news_from_svm():
  out = []
  if g.user:
    uid = session['user_id']
    if not uid in user_sim:
      return []
    reload_sim()
    plist = user_sim[uid]
    out = [db[x] for x in plist]
    out = sorted(out, key=lambda x: x['time'], reverse=True)
  return out

def reload_sim():
  global user_sim
  user_sim = {}
  if os.path.isfile(Config.user_sim_path):
    user_sim = pickle.load(open(Config.user_sim_path, 'rb'))

def news_search(qraw):
  qparts = list(cut_for_search(qraw))
  # use reverse index and accumulate scores
  scores = []
  for pid,p in db.items():
    score = sum(SEARCH_DICT[pid].get(q,0) for q in qparts)
    if score == 0:
      continue # no match whatsoever, dont include
    # give a small boost to more recent papers
    #TODO: add reward for recent news
    #score += 0.0001*p['tscore']
    scores.append((score, p))
  scores.sort(reverse=True, key=lambda x: x[0]) # descending
  out = [x[1] for x in scores if x[0] > 0]
  return out

def default_context(news, **kws):
  # prompt logic
  top_news = encode_json(news)
  ans = dict(news=top_news, numresults=len(top_news), totpapers=len(db), msg='')
  ans.update(kws)
  return ans



def encode_json(ps, n=200):

  ret = []
  for i in range(min(len(ps),n)):
    p = ps[i]
    struct = {}
    struct['title'] = p['title']
    struct['link'] = p['url']
    timee = p['time']
    timee =timee.strftime("%Y-%m-%d")
    struct['time'] = timee
    struct['id'] = p['id']
    struct['abstract'] = p['content'][:Config.snippetsize]+"..."
    ret.append(struct)
  return ret

if __name__ == "__main__":

    db = pickle.load(open(Config.db_path, 'rb'))
    for i, key in enumerate(db):
      db[key]['id'] = i
      timee = datetime.datetime.fromtimestamp(db[key]['time'])
      new_date = datetime.datetime(year=timee.year, month=timee.month, day=timee.day)
      db[key]['time'] = new_date
    SEARCH_DICT = pickle.load(open(Config.search_dict_path, 'rb'))
    user_sim = {}
    if os.path.isfile(Config.user_sim_path):
      user_sim = pickle.load(open(Config.user_sim_path, 'rb'))
    app.debug = False
    app.run(port=8080, host='10.141.246.30')
